using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.Ex.Model
{
#pragma warning disable 8500

    /// <summary>
    /// 通用柔性数组,非线程安全类
    /// </summary>
    /// <typeparam name="T">元素类型</typeparam>
    [DebuggerDisplay("Length = {Length}")]
    public class FlexibleArray<T> : IEnumerable<T>
    {
        /// <summary>
        /// 构造函数初始化
        /// </summary>
        /// <param name="buffer">原始数据buffer</param>
        /// <param name="startIndex">起始位置</param>
        /// <param name="length">数据长度</param>
        public FlexibleArray(T[] buffer, int startIndex, int length)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }

            if (startIndex < 0)
            {
                throw new ArgumentNullException(nameof(startIndex));
            }

            if (length < 0 || length > buffer.Length)
            {
                throw new ArgumentNullException(nameof(length));
            }

            if (startIndex + length > buffer.Length)
            {
                throw new ArgumentNullException(nameof(length));
            }

            this._buffer = new T[length];
            Array.Copy(buffer, startIndex, this._buffer, 0, length);
            this._length = length;
        }

        /// <summary>
        /// 构造函数初始化
        /// </summary>
        /// <param name="capcity">初始化容量</param>
        /// <param name="length">数据容量</param>
        public FlexibleArray(int capcity, int length)
        {
            if (length > capcity)
            {
                throw new ArgumentOutOfRangeException(nameof(length), $"数据长度\"{length}\"不能大小容量\"{capcity}\"");
            }
            this._buffer = new T[capcity];
            this._length = length;
        }

        /// <summary>
        /// 构造函数初始化,使用默认初始化容量
        /// </summary>
        public FlexibleArray()
            : this(100, 100)
        {

        }


        private T _defaultValue = default(T);
        /// <summary>
        /// 数据buffer
        /// </summary>
        private T[] _buffer = null;


        private int _count = 0;
        /// <summary>
        /// 获取或设置临时计数元素个数
        /// </summary>
        public int Count
        {
            get { return this._count; }
            set { this._count = value; }
        }

        /// <summary>
        /// 在现有Count值基础上加1
        /// </summary>
        /// <returns>旧值</returns>
        public int CountIncrement()
        {
            return Interlocked.Increment(ref this._count);
        }

        /// <summary>
        /// 在现有Count值基础上减1
        /// </summary>
        /// <returns>旧值</returns>
        public int CountDecrement()
        {
            return Interlocked.Decrement(ref this._count);
        }

        /// <summary>
        /// 在现有Count值基础上添加添加值
        /// </summary>
        /// <param name="value">添加值</param>
        /// <returns>旧值</returns>
        public int CountAdd(int value)
        {
            return Interlocked.Add(ref this._count, value);
        }

        /// <summary>
        /// 设置Count指定值
        /// </summary>
        /// <param name="value">目标值</param>
        /// <returns>旧值</returns>
        public int CountSet(int value)
        {
            return Interlocked.Exchange(ref this._count, value);
        }



        /// <summary>
        /// 获取或设置用户定义的数据
        /// </summary>
        public object Tag { get; set; }



        private int _length = 0;
        /// <summary>
        /// 获取或设置数据长度
        /// </summary>
        public int Length
        {
            get { return this._length; }
            set
            {
                if (value < 0 || value > this._buffer.Length)
                {
                    throw new ArgumentOutOfRangeException($"数据长度{value}不能超出容量{this._buffer.Length}.");
                }
                this._length = value;
            }
        }


        /// <summary>
        /// 获取或设置指定索引处的值
        /// </summary>
        /// <param name="index">索引</param>
        /// <returns></returns>
        public ref T this[int index]
        {
#if !NET4_0
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
            get
            {
                if (index < 0 || index >= this._length)
                {
                    throw new IndexOutOfRangeException($"索引值\"{index}\"超出了数组范围,数组长度\"{this._length}\"");
                }

                return ref this._buffer[index];
            }
            //[MethodImpl(MethodImplOptions.AggressiveInlining)]
            //set
            //{
            //    if (index < 0 || index >= this._length)
            //    {
            //        throw new IndexOutOfRangeException();
            //    }

            //    *(this.DataPtr + index) = value;
            //}
        }

        /// <summary>
        /// 获取第一个值,长度为0时抛出异常
        /// </summary>
        /// <returns></returns>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public ref T First()
        {
            if (this.Length == 0)
            {
                throw new ArgumentException("没有数据");
            }
            else
            {
                return ref this._buffer[0];
            }
        }

        /// <summary>
        /// 获取第一个值或长度为0时取类型T的默念值
        /// </summary>
        /// <returns></returns>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public ref T FirstOrDefault()
        {
            if (this.Length == 0)
            {
                return ref this._defaultValue;
            }
            else
            {
                return ref this._buffer[0];
            }
        }

        /// <summary>
        /// 获取最后一个值,长度为0时抛出异常
        /// </summary>
        /// <returns></returns>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public ref T Last()
        {
            if (this.Length == 0)
            {
                throw new ArgumentException("没有数据");
            }
            else
            {
                return ref this._buffer[this._length - 1];
            }
        }

        /// <summary>
        /// 获取最后一个值或长度为0时取类型T的默念值
        /// </summary>
        /// <returns>最后一个值或长度为0时取类型T的默念值</returns>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public ref T LastOrDefault()
        {
            if (this.Length == 0)
            {
                return ref this._defaultValue;
            }
            else
            {
                return ref this._buffer[this._length - 1];
            }
        }











        /// <summary>
        /// 检查容量是否足够,不够扩容,操作成功失败抛出异常
        /// </summary>
        /// <param name="length">需要的容量</param>
        /// <param name="copytOldData">扩容时是否将原来的数据拷贝到扩容后的数组buffer中[true:拷贝;false:不拷贝]</param>
        public void Check(int length, bool copytOldData = false)
        {
            this.ReallocArray(length, copytOldData);
        }

        /// <summary>
        /// 检查容量是否足够,不够尝试扩容,操作成功返回true,失败返回false
        /// </summary>
        /// <param name="length">需要的容量</param>
        /// <param name="copytOldData">扩容时是否将原来的数据拷贝到扩容后的数组buffer中[true:拷贝;false:不拷贝]</param>
        /// <returns>操作成功返回true,失败返回false</returns>
        public bool TryCheck(int length, bool copytOldData = false)
        {
            try
            {
                this.ReallocArray(length, copytOldData);
                return true;
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
                return false;
            }
        }

        /// <summary>
        /// 检查容量是否足够,不够扩容.不调整或调整成功正常则设置数据长度为调整长度,操作成功失败抛出异常
        /// </summary>
        /// <param name="length">需要的元素个数</param>
        /// <param name="copytOldData">扩容时是否将原来的数据拷贝到扩容后的数组buffer中[true:拷贝;false:不拷贝]</param>
        public void CheckSetLength(int length, bool copytOldData = false)
        {
            this.ReallocArray(length, copytOldData);
            this._length = length;
        }

        /// <summary>
        /// 检查容量是否足够,不够尝试扩容,操作成功返回true,失败返回false
        /// </summary>
        /// <param name="length">需要的元素个数</param>
        /// <param name="copytOldData">扩容时是否将原来的数据拷贝到扩容后的数组buffer中[true:拷贝;false:不拷贝]</param>
        /// <returns>操作成功返回true,失败返回false</returns>
        public bool TryCheckSetLength(int length, bool copytOldData = false)
        {
            try
            {
                this.ReallocArray(length, copytOldData);
                this._length = length;
                return true;
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
                return false;
            }
        }

        private void ReallocArray(int length, bool copytOldData)
        {
            if (length < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(length), $"元数个数不能小于0,值\"{length}\"无效.");
            }

            if (this._buffer.Length < length)
            {
                var buffer = new T[length];
                if (copytOldData)
                {
                    Array.Copy(this._buffer, 0, buffer, 0, this._buffer.Length);
                }
                this._buffer = buffer;
            }
        }



        /// <summary>
        /// 创建一个数据流
        /// </summary>
        /// <param name="access">访问方式</param>
        /// <returns>数据流</returns>
        public Stream CreateStream(FileAccess access)
        {
            if (TypeEx.GetPrimitiveDataType(default(T)) == PrimitiveDataTypes.Byte)
            {
                byte[] buffer = (byte[])((object)this._buffer);
                return new MemoryStream(buffer, 0, this._length, access == FileAccess.Write);
            }
            else
            {
                throw new InvalidOperationException("仅支持数据元素类型为byte.");
            }
        }

        /// <summary>
        /// 创建一个数据流
        /// </summary>
        /// <param name="length">需要的数据元素个数</param>
        /// <param name="access">访问方式</param>
        /// <returns>数据流</returns>
        public Stream CreateStream(int length, FileAccess access)
        {
            if (TypeEx.GetPrimitiveDataType(default(T)) == PrimitiveDataTypes.Byte)
            {
                if (length > this._buffer.Length)
                {
                    this.ReallocArray(length, true);
                }

                byte[] buffer = (byte[])((object)this._buffer);
                return new MemoryStream(buffer, 0, this._length, access == FileAccess.Write);
            }
            else
            {
                throw new InvalidOperationException("仅支持数据元素类型为byte.");
            }
        }




#if !NET4_0

        /// <summary>
        /// 获取当前数据长度的数据片
        /// </summary>
        /// <returns>数据片</returns>
        public Span<T> GetSpan()
        {
            return new Span<T>(this._buffer, 0, this._length);
        }

        /// <summary>
        /// 获取从0开始一指定数据长度的数据片
        /// </summary>
        /// <param name="count">指定数据长度</param>
        /// <param name="copytOldData">扩容时是否将原来的数据拷贝到扩容后的数组buffer中[true:拷贝;false:不拷贝]</param>
        /// <returns></returns>
        public Span<T> GetSpan(int count, bool copytOldData = false)
        {
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException($"参数值无效,count:{count},当前数据长度:{this._length}");
            }

            this.ReallocArray(count, copytOldData);
            return new Span<T>(this._buffer, 0, count);
        }

        /// <summary>
        /// 获取指定起始位置和数据长度的数据片
        /// </summary>
        /// <param name="startIndex">起始位置</param>
        /// <param name="count">指定数据长度</param>
        /// <param name="copytOldData">扩容时是否将原来的数据拷贝到扩容后的数组buffer中[true:拷贝;false:不拷贝]</param>
        /// <returns></returns>
        public Span<T> GetSpan(int startIndex, int count, bool copytOldData = false)
        {
            if (startIndex < 0 || count < 0)
            {
                throw new ArgumentOutOfRangeException($"参数值无效,startIndex:{startIndex},count:{count},当前数据长度:{this._length}");
            }

            this.ReallocArray(startIndex + count, copytOldData);
            return new Span<T>(this._buffer, startIndex, count);
        }
#endif

#if CORE
        /// <summary>
        /// 用指定的值填充到当前对象数据指针指向的内容
        /// </summary>
        /// <param name="value">填充值</param>        
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public void Fill(T value)
        {
            if (this._buffer != null)
            {
                Array.Fill(this._buffer, value, 0, this._length);
            }
        }

        /// <summary>
        /// 用指定的值填充到当前对象数据指针指向的内容
        /// </summary>
        /// <param name="startIndex">起始位置</param>
        /// <param name="count">填充个数</param>
        /// <param name="value">填充值</param>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public void Fill(int startIndex, int count, T value)
        {
            if (startIndex < 0 ||
                count < 0 ||
                startIndex + count > this._buffer.Length)
            {
                throw new ArgumentOutOfRangeException($"参数值无效,startIndex:{startIndex},count:{count},当前数据长度:{this._length}");
            }

            Array.Fill(this._buffer, value, startIndex, count);
        }
#else
        /// <summary>
        /// 用指定的值填充到当前对象数据指针指向的内容
        /// </summary>
        /// <param name="value">填充值</param>        
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public void Fill(T value)
        {
            if (this._buffer != null)
            {
                for (int i = 0; i < this._length; i++)
                {
                    this._buffer[i] = value;
                }
            }
        }

        /// <summary>
        /// 用指定的值填充到当前对象数据指针指向的内容
        /// </summary>
        /// <param name="startIndex">起始位置</param>
        /// <param name="count">填充个数</param>
        /// <param name="value">填充值</param>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public void Fill(int startIndex, int count, T value)
        {
            if (startIndex < 0 ||
                count < 0 ||
                startIndex + count > this._buffer.Length)
            {
                throw new ArgumentOutOfRangeException($"参数值无效,startIndex:{startIndex},count:{count},当前数据长度:{this._length}");
            }

            int endIndex = startIndex + count;
            if (endIndex > this._buffer.Length)
            {
                throw new ArgumentOutOfRangeException($"参数值无效,startIndex:{startIndex},count:{count},当前数据长度:{this._length}");
            }

            for (int i = startIndex; i < endIndex; i++)
            {
                this._buffer[i] = value;
            }
        }
#endif

        /// <summary>
        /// 清除当前对象数据指针指向的内容
        /// </summary>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public void Clear()
        {
            if (this._buffer != null)
            {
                Array.Clear(this._buffer, 0, this._length);
            }
        }

        /// <summary>
        /// Copies the contents of this span into a new array.  This heap
        /// allocates, so should generally be avoided, however it is sometimes
        /// necessary to bridge the gap with APIs written in terms of arrays.
        /// </summary>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public T[] ToArray()
        {
            if (this._length <= 0)
            {
#if NET4_0
                return ArrayEx.Empty<T>();
#else
                return Array.Empty<T>();
#endif
            }

            var destination = new T[this._length];
            Array.Copy(this._buffer, 0, destination, 0, this._length);
            return destination;
        }

        /// <summary>
        /// 将数据拷贝到列表中
        /// </summary>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public List<T> ToList()
        {
            if (this._length <= 0)
            {
                return new List<T>();
            }

            return this._buffer.Take(this._length).ToList();
        }



        /// <summary>
        /// 将当前数据复制到目标数据中,作参数检查
        /// </summary>
        /// <param name="destination">目标数据</param>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public void CopyTo(FlexibleArray<T> destination)
        {
            if (this._length > destination._length)
            {
                throw new ArgumentOutOfRangeException("目标长度不足");
            }

            Array.Copy(this._buffer, 0, destination._buffer, 0, this._length);
        }

        /// <summary>
        /// 将当前数据复制到目标数据中,作参数检查
        /// </summary>
        /// <param name="soucrceStartIndex">当前数据起始位置</param>
        /// <param name="destination">目标数据</param>
        /// <param name="destinationStartIndex">目标数据起始位置</param>
        /// <param name="count">复制元素个数</param>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public void CopyTo(int soucrceStartIndex, FlexibleArray<T> destination, int destinationStartIndex, int count)
        {
            if (soucrceStartIndex < 0 || soucrceStartIndex >= this._length)
            {
                throw new ArgumentOutOfRangeException("复制的源数据起始位置超出了当前数据容量范围");
            }

            int soucrceStartIndexL = soucrceStartIndex;

            if (soucrceStartIndexL + count > this._length)
            {
                throw new ArgumentOutOfRangeException("复制的数据长度超出了当前数据容量");
            }


            long destinationStartIndexL = destinationStartIndex;
            if (destinationStartIndexL < 0 || destinationStartIndexL >= destination._length)
            {
                throw new ArgumentOutOfRangeException("复制的目标数据起始位置超出了当前数据容量范围");
            }

            if (destinationStartIndex + count > destination._length)
            {
                throw new ArgumentOutOfRangeException("复制的数据长度超出了目标数据容量");
            }

            Array.Copy(this._buffer, soucrceStartIndex, destination._buffer, destinationStartIndex, count);
        }

        /// <summary>
        /// 从指针数据源中将数据复制到当前数组中,作参数检查
        /// </summary>
        /// <param name="sourcePtr">指针数据源</param>
        /// <param name="sourceElecount">指针数据元素个数</param>
        /// <exception cref="ArgumentOutOfRangeException">参数越界异常</exception>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        unsafe public void CopyFrom(T* sourcePtr, int sourceElecount)
        {
            if (sourceElecount > this._length)
            {
                throw new ArgumentOutOfRangeException($"源长度{sourceElecount}大于目标长度{this._length}不足");
            }

            fixed (T* destPtr = this._buffer)
            {
                NativeMemory.Copy(sourcePtr, destPtr, (nuint)(sourceElecount * sizeof(T)));
            }
        }




        /// <summary>
        /// 分片返回一个固定的柔性数组
        /// </summary>
        /// <param name="start">The index at which to begin this slice.</param>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;Length).
        /// </exception>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public FlexibleArray<T> Slice(int start)
        {
            if (start < 0 || start > this._length)
            {
                throw new ArgumentOutOfRangeException(nameof(start), $"start:{start},Length:{this._length}");
            }

            return new FlexibleArray<T>(this._buffer, start, this._length - start);
        }

        /// <summary>
        /// 分片返回一个固定的柔性数组
        /// </summary>
        /// <param name="start">The index at which to begin this slice.</param>
        /// <param name="length">The desired length for the slice (exclusive).</param>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Thrown when the specified <paramref name="start"/> or end index is not in range (&lt;0 or &gt;Length).
        /// </exception>
#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public FlexibleArray<T> Slice(int start, int length)
        {
            if (start < 0 || start > this._length)
            {
                throw new ArgumentOutOfRangeException(nameof(start), $"start:{start},length:{length},Length:{this._length}");
            }

            if (start + length > this._length)
            {
                throw new ArgumentOutOfRangeException(nameof(length), $"start:{start},length:{length},Length:{this._length}");
            }

            return new FlexibleArray<T>(this._buffer, start, length);
        }






        /// <summary>
        /// 重写ToString
        /// </summary>
        /// <returns>String</returns>
        public override string ToString()
        {
            var sb = new StringBuilder();
            for (int i = 0; i < this._length; i++)
            {
                if (sb.Length > 0)
                {
                    sb.Append(',');
                }
                sb.Append(this._buffer[i]);
            }
            return sb.ToString();
        }

        /// <summary>
        /// 重写ToString
        /// </summary>
        /// <returns>String</returns>
        public string ToString(char separator)
        {
            var sb = new StringBuilder();
            for (int i = 0; i < this._length; i++)
            {
                if (sb.Length > 0)
                {
                    sb.Append(separator);
                }
                sb.Append(this._buffer[i]);
            }
            return sb.ToString();
        }

        /// <summary>
        /// 重写ToString
        /// </summary>
        /// <returns>String</returns>
        public string ToString(string separator)
        {
            var sb = new StringBuilder();
            for (int i = 0; i < this._length; i++)
            {
                if (sb.Length > 0)
                {
                    sb.Append(separator);
                }
                sb.Append(this._buffer[i]);
            }
            return sb.ToString();
        }



        #region IEnumerable

        /// <summary>
        /// IEnumerable
        /// </summary>
        /// <returns></returns>
        public IEnumerator<T> GetEnumerator()
        {
            return new FlexibleArrayEnumerator(this);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        private class FlexibleArrayEnumerator : IEnumerator<T>
        {
            private readonly FlexibleArray<T> _arr;
            private int _index = -1;
            public FlexibleArrayEnumerator(FlexibleArray<T> arr)
            {
                this._arr = arr;
            }

            public T Current
            {
                get
                {
                    return this._arr._buffer[this._index];
                }
            }

            object IEnumerator.Current
            {
                get
                {
                    return this._arr._buffer[this._index];
                }
            }

            public void Dispose()
            {

            }

            public bool MoveNext()
            {
                this._index++;
                if (this._index < this._arr._length)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }

            public void Reset()
            {
                this._index = -1;
            }
        }

        #endregion

    }
}
